﻿using UnityEngine;
using System;
using System.IO;
using System.Text;
using System.Collections;
using System.Collections.Generic;


public class Kingdom : ISerializable
{
	public static int MaxTechDev = 3;

	private Galaxy.Civilization m_Stage;
	private Race m_Race;
	private StarSystem m_Home;
	private bool m_Append;									// auto append technology development to MaxTechDev
	private string m_KingName;						// king's name of the kingdom
	private int m_ID;

	private List<SolarPlanet> m_Planets = new List<SolarPlanet>();		// kingdom owned planets
	private List<KingdomTech> m_Tech = new List<KingdomTech>();
	private List<Fleet> m_Fleets = new List<Fleet>();
	private Hashtable m_Dispatchables = new Hashtable();

	private List<TechByCost> m_Dev = new List<TechByCost>();					// technologies under development
	private List<TechByCost> m_Owned = new List<TechByCost>();				// newly owned technology in this turn
	private List<Technology> m_Unlocked = new List<Technology>();			// newly found technology due to newly owned technologies
	private double m_SciPower;																				// the scientific power accumulated by far, for technology advancement
	private double m_Reach;																						// how far can kingdom's technology reach universe
	private double m_Drive;																						// how many light years can travel per turn
	private double m_DriveBonus;																			// extra drive bonus

	private double m_Gold;									// kingdom's total gold
	private double m_Command;								// kingdom's fleet command points

	private int m_FillDev;								// how many facilities to be refilled in planets' dev queue


	// ISerializable
	ulong ISerializable.Serial
	{
		get { return (ulong)m_ID; }
	}

	// getters and setters
	#region getters and setters
	public Galaxy.Civilization Stage
	{
		get { return m_Stage; }
		set { m_Stage = value; }
	}

	public Race Race
	{
		get { return m_Race; }
		set { m_Race = value; }
	}

	public StarSystem Home
	{
		get { return m_Home; }
		set { m_Home = value; }
	}

	public bool Append
	{
		get { return m_Append; }
		set { m_Append = value; }
	}

	public string KingName
	{
		get { return m_KingName; }
		set { m_KingName = value; }
	}

	public int KingdomID
	{
		get { return m_ID; }
		set { m_ID = value; }
	}

	public List<SolarPlanet> Planets
	{
		get { return m_Planets; }
		set { m_Planets = value; }
	}

	public List<KingdomTech> Tech
	{
		get { return m_Tech; }
		set { m_Tech = value; }
	}

	public List<Fleet> Fleets
	{
		get { return m_Fleets; }
		set { m_Fleets = value; }
	}

	public Hashtable Dispatchables
	{
		get { return m_Dispatchables; }
		set { m_Dispatchables = value; }
	}

	public List<TechByCost> Dev
	{
		get { return m_Dev; }
		set { m_Dev = value; }
	}

	public List<TechByCost> Owned
	{
		get { return m_Owned; }
		set { m_Owned = value; }
	}

	public List<Technology> Unlocked
	{
		get { return m_Unlocked; }
		set { m_Unlocked = value; }
	}

	public double SciPower
	{
		get { return m_SciPower; }
		set { m_SciPower = value; }
	}

	public double Reach
	{
		get { return m_Reach; }
		set { m_Reach = value; }
	}

	public double Drive
	{
		get { return m_Drive; }
		set { m_Drive = value; }
	}

	public double DriveBonus
	{
		get { return m_DriveBonus; }
		set { m_DriveBonus = value; }
	}

	public double Gold
	{
		get { return m_Gold; }
		set { m_Gold = value; }
	}

	public double Command
	{
		get { return m_Command; }
		set { m_Command = value; }
	}

	public int FillDev
	{
		get { return m_FillDev; }
		set { m_FillDev = value; }
	}
	#endregion


	// contructors
	public Kingdom()
	{
	}

	public Kingdom(Race race)
	{
		m_Race = race;

		// initialize properties
		m_Append = true;
		m_Reach = 2000.0;								// initial reaching capability
		m_Drive = 300;
		m_DriveBonus = 0;
		m_Gold = 50.0;
		m_FillDev = 1;
		m_KingName = GetRandKingName();

		// LoadFullTechnology();
		m_Stage = Galaxy.Civilization.Primitive;
		//InitializeKingdom();

	}

	// sub classes
	public class SolarPlanet
	{
		public StarSystem pSolar;
		public int pDistance;
		public int pTurn;
		public List<Planet> plPlanets = new List<Planet>();

		public SolarPlanet(StarSystem solar)
		{
			pSolar = solar;
		}

		public bool Add(Planet plt)
		{
			foreach (Planet pl in plPlanets)
				if (pl.PlanetID == plt.PlanetID)
					return false;

			plPlanets.Add(plt);
			plPlanets.Sort(new StarSystem.OrbitSorter());

			return true;
		}
	}

	public class HomeSorter : IComparer<SolarPlanet>
	{
		public int Compare(SolarPlanet x, SolarPlanet y)
		{
			return Compare(x.pDistance, y.pDistance);
		}

		public int Compare(int x, int y)
		{
			return x - y;					// in reverse order
		}
	}

	public class TechByCost
	{
		public double pCost;
		public Technology pTech;
		public Technology.TechStatus pStatus;
		public string pName;										// the display name of technology
		public double pSciPower;								// Scientific power consumed, technology will be owned when pSciPower = pCost

		public TechByCost(double cost, Technology tech, Technology.TechStatus status, string dname)
		{
			pCost = cost;
			pTech = tech;
			pStatus = status;

			pName = dname;
		}

		public TechByCost(double cost, Technology tech, Technology.TechStatus status) : this(cost, tech, status, Utility.SplitName(tech.Name.ToString()).Trim())
		{

		}
	}

	public class CostSorter : IComparer<TechByCost>
	{
		public int Compare(TechByCost x, TechByCost y)
		{
			return Compare((int)(x.pTech.Cost * 100), (int)(y.pTech.Cost * 100));
		}

		public int Compare(int x, int y)
		{
			return x - y;
		}
	}

	public class KingdomTech
	{
		public Technology.TechCategory pCategory;
		public List<TechByCost> plTech = new List<TechByCost>();

		public KingdomTech(Technology.TechCategory category)
		{
			pCategory = category;
		}

		public bool Add(double cost, Technology tech, Technology.TechStatus status)
		{
			foreach (TechByCost tbc in plTech)
			{
				if (tbc.pTech.Name == tech.Name)
					return false;														// duplicated technology detected
			}

			plTech.Add(new TechByCost(cost, tech, status));
			plTech.Sort(new CostSorter());

			return true;
		}

	}

	public class CategorySorter : IComparer<KingdomTech>
	{
		public int Compare(KingdomTech x, KingdomTech y)
		{
			return Compare((int)x.pCategory, (int)y.pCategory);
		}

		public int Compare(int x, int y)
		{
			return x - y;
		}
	}


	// class methods
	public bool InitializeKingdom()
	{
		AddTechnology(Technology.Table[0], Technology.TechStatus.Owned);			// ColonyBase
		AddTechnology(Technology.Table[1], Technology.TechStatus.Owned);			// Advanced Electronics
		AddTechnology(Technology.Table[3], Technology.TechStatus.Owned);			// Advanced Laser Technology
		AddTechnology(Technology.Table[6], Technology.TechStatus.Owned);			// Advanced Physics
		AddTechnology(Technology.Table[8], Technology.TechStatus.Owned);			// Advanced Energy Research
		AddTechnology(Technology.Table[10], Technology.TechStatus.Owned);			// Advanced Engineering
		//AddTechnology(Technology.Table[15], Technology.TechStatus.Owned);			// Advanced Metallurgy
		//AddTechnology(Technology.Table[18], Technology.TechStatus.Owned);			// Advanced Contruction
		AddTechnology(Technology.Table[19], Technology.TechStatus.Owned);			// StarBase
		AddTechnology(Technology.Table[20], Technology.TechStatus.Owned);			// Advanced Ecology
		//AddTechnology(Technology.Table[28], Technology.TechStatus.Owned);			// Advanced Optics
		//AddTechnology(Technology.Table[31], Technology.TechStatus.Owned);			// Advanced Manufacturing
		//AddTechnology(Technology.Table[33], Technology.TechStatus.Owned);			// Advanced Parallel Computing

		UpdateTechProfile(m_Stage);

		PrepareHome();

		return true;
	}

	public void PrepareHome()
	{
		if (Core.mainGalaxy.Homes == null || Core.mainGalaxy.Homes.Count == 0)
		{
			Debug.Log("Failed to address home position!");
			return;
		}

		bool found = false;

		foreach (StarSystem ss in Core.mainGalaxy.Solars)
			if (ss.Location == Core.mainGalaxy.Homes[0])
			{
				m_Home = ss;
				AddColonyPlanet(ss);

				found = true;
			}

		if (!found)
		{
			Debug.Log("Failed to allocate home star!");
			return;
		}

		Core.mainGalaxy.Homes.RemoveAt(0);
		ScanReachables();

		m_Home.AddNewColony(this, 1);			// initial colony base

	}

	public void ScanReachables()
	{
	  Point3D px;
	  Point3D py;
	  double distance;

		List<StarSystem> lsolar = new List<StarSystem>();

	  foreach (SolarPlanet sp in m_Planets)
	    if (sp.pSolar == m_Home || Kingdom.SolarOwned(this, sp.pSolar))
	      foreach (StarSystem ss in Core.mainGalaxy.Solars)
	      {
	        px = ss.Location;
	        py = sp.pSolar.Location;

	        distance = Math.Sqrt(Math.Pow(px.X - py.X, 2) + Math.Pow(px.Y - py.Y, 2) + Math.Pow(px.Z - py.Z, 2));

					if (distance <= m_Reach)
						lsolar.Add(ss);
	      }

		foreach (StarSystem sys in lsolar)
			AddColonyPlanet(sys);
	}
	
	public List<Technology> UpdateTechProfile()
	{
		return UpdateTechProfile(m_Stage);
	}

	public List<Technology> UpdateTechProfile(Galaxy.Civilization stage)
	{
		List<Technology> lret = new List<Technology>();
		List<Technology> lTech = new List<Technology>();

		AdvancingStage(stage);

		foreach (Technology mtech in Technology.Table)
		{
			if (!(mtech.Stage > stage))				// only update current stage technologies
			{
				if (!(mtech.Dependent.Length > 0))
					lTech.Add(mtech);
				else
				{
					int count = 0;
					foreach (GameEnums.TechName depend in mtech.Dependent)			// check to see if all mother technology is already present
					{
						foreach (KingdomTech ktech in m_Tech)
							foreach (TechByCost tbc in ktech.plTech)
							{
								if (tbc.pTech.Name == depend && tbc.pStatus > Technology.TechStatus.Known)		// if all mother technologies are already developed
									count++;
							}
					}

					if (count == mtech.Dependent.Length)
						lTech.Add(mtech);
				}
			}
		}

		//foreach (Technology tec in lTech)
		//  Debug.Log(tec.Name.ToString());

		// check to see if all qualified technologies are already in
		if (lTech.Count > 0)
		{
			bool found;

			for (int i = 0; i < lTech.Count; i++)
			{
				found = false;

				foreach (KingdomTech kt in m_Tech)
					foreach (TechByCost te in kt.plTech)
					{
						if (te.pTech.Name == lTech[i].Name)
						{
							if (te.pStatus == Technology.TechStatus.Unknown)
							{
								te.pStatus = Technology.TechStatus.Known;			// to update qualified unknown technology to known
								lret.Add(te.pTech);														// to store updated technology for return
							}

							found = true;
						}
					}

				if (found)
					lTech.RemoveAt(i--);
			}
		}

		//foreach (Technology tec in lTech)
		//  Debug.Log(tec.Name.ToString());

		foreach (Technology technology in lTech)
			if (AddTechnology(technology, Technology.TechStatus.Known))
				lret.Add(technology);

		return lret;
	}

	public bool LoadFullTechnology()
	{
		foreach (Technology tech in Technology.Table)
			AddTechnology(tech, Technology.TechStatus.Owned);

		return true;
	}

	public bool AddColonyPlanet(StarSystem solar)
	{
		return AddColonyPlanet(solar, null);
	}

	public bool AddColonyPlanet(StarSystem solar, Planet star)
	{
		bool found = false;
		int[] disturn = new int[2] { 0, 0 };

		foreach (SolarPlanet sp in m_Planets)
			if (sp.pSolar == solar)
			{
				found = true;								// solar already stored, try to add star

				if (star != null && sp.Add(star))
					return true;
				else
					return false;
			}

		if (!found)											// to add a new solar
		{
			SolarPlanet spl = new SolarPlanet(solar);

			if (star != null)
				spl.Add(star);

			disturn = Kingdom.GetDistanceTurn(m_Home, solar, m_Drive + m_DriveBonus);
			spl.pDistance = disturn[0] / 1000;
			spl.pTurn = disturn[1];
			m_Planets.Add(spl);
			m_Planets.Sort(new HomeSorter());
		}

		return true;
	}

	public bool AddTechnology(Technology tech, Technology.TechStatus status)
	{
		return AddTechnology(tech, status, tech.Cost);
	}

	public bool AddTechnology(Technology tech, Technology.TechStatus status, double cost)			// cost can be different from technology's default cost
	{
		bool found = false;
		KingdomTech kTech;

		foreach (KingdomTech kt in m_Tech)
		{
			if (kt.pCategory == tech.Category)
			{
				found = true;

				if (!kt.Add(cost, tech, status))
					return false;														// duplicated technology found
			}
		}

		if (!found)
		{
			kTech = new KingdomTech(tech.Category);
			kTech.Add(cost, tech, status);
			m_Tech.Add(kTech);
			m_Tech.Sort(new CategorySorter());
		}

		return true;
	}

	public bool ResetTechCost(TechByCost tbc)				// to reset a certain kingdom's technology cost back to default
	{
		tbc.pCost = tbc.pTech.Cost;

		return true;
	}

	public bool AdvancingStage(Galaxy.Civilization stage)
	{
		if (stage == Galaxy.Civilization.FarBeyond)
			return false;							// no more stage to advance

		int tcount = 0;
		int kcount = 0;

		foreach (Technology tech in Technology.Table)
		{
			if (tech.Stage == stage && tech.Key)
				tcount++;
		}

		foreach (KingdomTech kt in m_Tech)
			foreach (TechByCost tbc in kt.plTech)
			{
				if (tbc.pTech.Stage == stage && tbc.pTech.Key)
					kcount++;
			}

		if (kcount == tcount)									// all key technology developed, stage will be advanced
			m_Stage = (Galaxy.Civilization)((int)m_Stage + 1);

		return true;
	}

	public virtual void OnBeginTurn()
	{
		// testing
		//foreach (SolarPlanet sp in m_Planets)
		//  if (SolarOwned(this, sp.pSolar))
		//  {
		//    foreach (Fleet fl in sp.pSolar.Docking)
		//      Debug.Log(m_Race.RaceType.ToString() + ", Docking: " + "#" + fl.FleetID.ToString() + fl.DisplayName);

		//    foreach (Fleet flt in sp.pSolar.Launching)
		//    {
		//      Debug.Log(m_Race.RaceType.ToString() + ", Launching: " + "#" + flt.FleetID.ToString() + flt.DisplayName);

		//      foreach (Fleet fd in flt.Dest.Dests)
		//        Debug.Log(m_Race.RaceType.ToString() + ", Destined: " + "#" + fd.FleetID.ToString() + fd.DisplayName);
		//    }
		//  }
	}

	public virtual void OnEndTurn()
	{
		foreach (SolarPlanet spl in m_Planets)
			foreach (Planet plt in spl.plPlanets)
			{
				plt.OnEndTurn();
			}
	}

	public virtual void OnTurn()
	{
		// handle kingdom planet turn actions
		foreach (SolarPlanet spl in m_Planets)
			foreach (Planet plt in spl.plPlanets)
			{
				if (plt.Kingdom == this)
					plt.OnTurn();
			}
		
		// calculating scientific power accumulated
		m_SciPower += 50.0;														// temporarily assigned scientific power growth

		// randomly pick technology for development
		if (m_Append && m_Dev.Count < MaxTechDev)			// if !m_Append, m_Dev will be re-filled manually by other methods
			RefillRandomTech(MaxTechDev - m_Dev.Count, m_Dev);

		m_Owned = ConsumeSciPower();
		m_Unlocked = UpdateTechProfile();
		
	}

	public List<TechByCost> ConsumeSciPower()
	{
		List<TechByCost> ltech = new List<TechByCost>();

		if (m_Dev.Count == 0)
			return ltech;

		for (int i = 0; i < m_Dev.Count; i++)
		{
			m_Dev[i].pSciPower += m_SciPower;
			if (m_Dev[i].pSciPower >= m_Dev[i].pCost)
			{
				m_Dev[i].pStatus = Technology.TechStatus.Owned;				// development of this technology is completed
				ltech.Add(m_Dev[i]);																	// to store newly owned technology
				m_Dev.RemoveAt(i--);
			}
		}

		m_SciPower = 0.0;							// scientific power consumed

		return ltech;
	}

	public void RefillRandomTech(int numTech)
	{
		RefillRandomTech(numTech, new List<TechByCost>());
	}

	public void RefillRandomTech(int numTech, List<TechByCost> exclude)
	{
		List<TechByCost> ltemp = new List<TechByCost>();
		bool found = false;

		foreach (KingdomTech kt in m_Tech)
			foreach (TechByCost tb in kt.plTech)
			{
				if (tb.pStatus == Technology.TechStatus.Known)
				{
					foreach (TechByCost tbc in exclude)		// to exclude tech already under development
						if (tbc.pTech == tb.pTech)
							found = true;

					if (!found)
						ltemp.Add(tb);				// to temp-store all known technology for random pick
				}
			}

		if (ltemp.Count > 0)				// ltemp.Count == 0 could mean that there's no known technology
		{
			numTech = Math.Min(numTech, ltemp.Count);				// known technology may be less than numTech
			int idx = 0;

			for (int i = 0; i < numTech; i++)
			{
				idx = Utility.Random(ltemp.Count);				// to pick random known technology

				m_Dev.Add(ltemp[idx]);			// add the display name of randomly picked technology for return
				ltemp.RemoveAt(idx);
			}
		}
	}

	public bool SendColonyShip()
	{
		Debug.Log(m_Race.RaceType.ToString() + "SendColonyShip():Here1 Total Fleets=" + m_Fleets.Count.ToString());
		if (m_Fleets.Count == 0)
			return false;

		Debug.Log(m_Race.RaceType.ToString() + "SendColonyShip():Here2");

		List<StarSystem> lstars = new List<StarSystem>();
		List<Fleet> lcolony = new List<Fleet>();
		List<Fleet> lpost = new List<Fleet>();

		StarSystem star = m_Home;

		foreach (SolarPlanet sp in m_Planets)
			if (sp.pSolar.OwnedBy.Count == 0 || (sp.pSolar.OwnedBy.Contains(this) && !IsPlanetOwned(sp.pSolar)))			// solar without an owner
			{
				bool toadd = true;
				foreach (Fleet dest in sp.pSolar.Dests)
				{
					if (dest is ColonyShip && dest.Belong == this)
						toadd = false;			// there's already another colonyship heading to this solar
				}

				if (toadd)
					lstars.Add(sp.pSolar);
			}

		if (lstars.Count == 0)			// && not in war status with destination kingdom
			return false;							// all reachable solars have an owner

		foreach (Fleet fl in m_Fleets)
		{
			if (!fl.Launched && fl.GetType() == typeof(ColonyShip))
				lcolony.Add(fl);
			else if (!fl.Launched && fl.GetType() == typeof(OutpostShip))
				lpost.Add(fl);
		}

		Debug.Log(m_Race.RaceType.ToString() + " Colony: " + lcolony.Count.ToString());

		if (lcolony.Count > 0 && 0.2 > Utility.RandomDouble())
		{
			int ship = 0;

			if (0.25 > Utility.RandomDouble())
				ship = 2;			// 1/4 chance to launch 2 colony ship, if there's enough colonyship or unowned solars
			else
				ship = 1;

			ship = Math.Min(ship, lcolony.Count);
			ship = Math.Min(ship, lstars.Count);

			for (int i = 0; i < ship; i++)
			{
				star = lstars[i];
				PrepareLaunching(lcolony[i], star);
			}
		}
		else if (lpost.Count > 0 && 0.45 > Utility.RandomDouble())
		{
			star = lstars[Utility.Random(lstars.Count)];
			PrepareLaunching(lpost[0], star);
		}

		return true;
	}

	public virtual bool PrepareLaunching(Fleet fl, StarSystem dest)
	{
		List<Fleet> lflts = new List<Fleet>() { fl };

		return PrepareLaunching(lflts, dest);
	}

	public virtual bool PrepareLaunching(List<Fleet> flts, StarSystem dest)
	{
		foreach (Fleet fl in flts)
		{
			fl.Home.Docking.Remove(fl);
			fl.Home.Launching.Add(fl);
			fl.Home.Launching.Sort(new Fleet.SortRankSorter());
			dest.Dests.Add(fl);
			fl.Launched = true;

			fl.Dest = dest;
		}

		return true;
	}

	public virtual bool MovingFleets(List<Fleet> lflts)
	{


		return true;
	}


	// static methods
	public static bool DockingNewSolar(Fleet fl, StarSystem star, out bool destroy)
	{
		destroy = false;

		Debug.Log(fl.Belong.Race.RaceType.ToString() + " #" + fl.FleetID.ToString() + fl.DisplayName
			+ " Star: " + star.StarName + " OwnedBy: " + star.OwnedBy.Count.ToString());

		// TODO: need to add war logic here
		if (star.OwnedBy.Count == 0)
		{
			star.OwnedBy.Add(fl.Belong);

			Debug.Log("#" + fl.FleetID + " " + fl.DisplayName + " to be destroyed!");

			if (fl is OutpostShip)		// destroyed
				destroy = true;
		}

		fl.Home.Launching.Remove(fl);
		star.Docking.Add(fl);
		star.Docking.Sort(new Fleet.SortRankSorter());
		fl.Home = star;
		fl.Location = star.Location;
		fl.Turn = -1;

		fl.Dest.Dests.Remove(fl);
		fl.Dest = star;
		fl.Launched = false;

		return true;
	}

	public static bool MoveFleetLocation(Fleet fl)
	{
		Vector3 h = Utility.GetVector3(fl.Location);
		Vector3 d = Utility.GetVector3(fl.Dest.Location);

		double dist = (double)Vector3.Distance(h, d);
		Vector3 p = Vector3.MoveTowards(h, d, (float)(fl.Belong.Drive + fl.Belong.DriveBonus));

		fl.Location = new Point3D((int)p.x, (int)p.y, (int)p.z);
		fl.Turn = (int)Math.Ceiling(dist / (fl.Belong.Drive + fl.Belong.DriveBonus));

		Debug.Log(fl.Belong.Race.RaceType.ToString() + " #" + fl.FleetID.ToString() + " "
			+ fl.GetType().ToString() + " |" + fl.Home.StarName + fl.Home.Location.ToString() + " |"
			+ fl.Dest.StarName + fl.Dest.Location.ToString() + " |" + fl.Location.ToString()
			+ ", Dist: " + dist.ToString() + ", Dest: " + fl.Dest.OwnedBy.Count.ToString() 
			+ ", Turn: " + fl.Turn.ToString());


		return true;
	}

	public static string GetRandKingName()
	{
		if (0.25 > Utility.RandomDouble())
			return AddNameSuffix(Utility.GetUniqueFemaleName());
		else
			return AddNameSuffix(Utility.GetUniqueMaleName());
	}

	public static string AddNameSuffix(string name)
	{
		string fixname = "";

		int rand = Utility.Random(3);

		if (rand > 0)
			fixname = ((Planet.PlanetNaming)rand).ToString();

		name += " " + fixname;

		return name.Trim();
	}

	public static bool RegisterNewColony(Kingdom kd, Planet plt, bool cbase)
	{
		bool toadd = false;

		if (cbase && plt.Solar.SolarColony.ContainsKey(kd) && (int)plt.Solar.SolarColony[kd] > 0)
			toadd = true;
		else if (!cbase)				// colony ship
			toadd = true;

		if (!toadd)
		{
			Debug.Log("Do not have the colony base!");
			return false;
		}

		toadd = false;

		foreach (Kingdom.SolarPlanet sp in kd.Planets)
			if (sp.pSolar == plt.Solar)
				toadd = true;

		if (!toadd)
		{
			Debug.Log("Failed to find valid solar system to add new colony!");
			return false;
		}

		plt.Kingdom = kd;
		plt.Status = Planet.PlanetStatus.Conquered;

		// update to m_Planets here, to avoid enumeration/iteration conflict
		if (cbase)
			plt.Solar.AddNewColony(kd, -1);		// to consume colony base and add new planet
		else
			plt.Solar.FindMaxColonyBase();		// recalculate allowable colonybase after colony ship settled

		if (!SolarOwned(kd, plt.Solar))
			plt.Solar.OwnedBy.Add(kd);

		kd.AddColonyPlanet(plt.Solar, plt);									// to register conquered planet
		kd.ScanReachables();
		
		plt.UpdatePlanetFacilities();
		plt.UpdateDispatchables();
		plt.UpdateWarrables();

		return true;
	}

	public static int[] GetDistanceTurn(StarSystem home, StarSystem dest, double drive)
	{
		Vector3 h = Utility.GetVector3(home.Location);
		Vector3 d = Utility.GetVector3(dest.Location);

		double distance = (double)Vector3.Distance(h, d);

		int dist = (int)(distance * 1000);
		int turn = (int)Math.Ceiling(distance / drive);

		return new int[] { dist, turn };
	}

	public static bool SolarOwned(Kingdom kd, StarSystem star)
	{
		return star.OwnedBy.Contains(kd);
	}

	public static bool SolarFullyOwned(StarSystem star)
	{
		foreach (Planet pl in star.Planets)
			if (pl.Status == Planet.PlanetStatus.None)
				return false;

		return true;
	}


	public static bool IsPlanetOwned(StarSystem star)
	{
		foreach (Planet pl in star.Planets)
			if (pl.Status != Planet.PlanetStatus.None)
				return true;

		return false;
	}

	public static bool IsOnePlanetLeft(StarSystem star)
	{
		int cnt = 0;

		foreach (Planet pl in star.Planets)
			if (!pl.Giant && pl.Status == Planet.PlanetStatus.None)
				cnt++;

		if (cnt == 1)
			return true;

		return false;
	}

	public static bool DestroyFleet(Fleet fl)
	{
		if (fl.Home.Launching.Contains(fl))
			fl.Home.Launching.Remove(fl);

		if (fl.Home.Docking.Contains(fl))
			fl.Home.Docking.Remove(fl);

		if (fl.Dest.Dests.Contains(fl))
			fl.Dest.Dests.Remove(fl);

		fl.Belong.Fleets.Remove(fl);

		Core.Fleets.Remove(fl.FleetID);

		Debug.Log(fl.Belong.Race.RaceType.ToString() + " #" + fl.FleetID.ToString() + " " + fl.GetType().ToString() + " destroyed!");

		return true;
	}


	// ISerializable
	public virtual void Serialize(GenericWriter writer)
	{
		writer.Write((int)0); // version

		writer.Write((int)MaxTechDev);
		writer.Write((int)m_Stage);
		writer.Write((Race)m_Race);
		writer.Write((StarSystem)m_Home);
		writer.Write((bool)m_Append);
		writer.Write((string)m_KingName);

		writer.Write((int)m_ID);

		writer.Write((int)m_Planets.Count);
		foreach (SolarPlanet sp in m_Planets)
		{
			writer.Write((StarSystem)sp.pSolar);
			writer.Write((int)sp.pDistance);
			writer.Write((int)sp.pTurn);

			writer.Write((int)sp.plPlanets.Count);
			foreach (Planet pl in sp.plPlanets)
			{
				writer.Write((Planet)pl);
			}
		}

		writer.Write((int)m_Tech.Count);
		foreach (KingdomTech kt in m_Tech)
		{
			writer.Write((int)kt.pCategory);

			writer.Write((int)kt.plTech.Count);
			foreach (TechByCost tbc in kt.plTech)
			{
				writer.Write((double)tbc.pCost);
				writer.Write((Technology)tbc.pTech);
				writer.Write((int)tbc.pStatus);
				writer.Write((string)tbc.pName);
				writer.Write((double)tbc.pSciPower);
			}
		}

		writer.Write((int)m_Fleets.Count);
		foreach (Fleet fl in m_Fleets)
			writer.Write((Fleet)fl);

		writer.Write((int)m_Dispatchables.Count);
		foreach (DictionaryEntry de in m_Dispatchables)
		{
			writer.Write(((Type)de.Key).FullName);
			writer.Write((int)de.Value);
		}

		writer.Write((int)m_Dev.Count);
		foreach (TechByCost tb in m_Dev)
		{
			writer.Write((double)tb.pCost);
			writer.Write((Technology)tb.pTech);
			writer.Write((int)tb.pStatus);
			writer.Write((string)tb.pName);
			writer.Write((double)tb.pSciPower);
		}

		writer.Write((int)m_Owned.Count);
		foreach (TechByCost tc in m_Owned)
		{
			writer.Write((double)tc.pCost);
			writer.Write((Technology)tc.pTech);
			writer.Write((int)tc.pStatus);
			writer.Write((string)tc.pName);
			writer.Write((double)tc.pSciPower);
		}

		writer.Write((int)m_Unlocked.Count);
		foreach (Technology tech in m_Unlocked)
			writer.Write((Technology)tech);

		writer.Write((double)m_SciPower);
		writer.Write((double)m_Reach);
		writer.Write((double)m_Drive);
		writer.Write((double)m_DriveBonus);
		writer.Write((double)m_Gold);
		writer.Write((double)m_Command);

		writer.Write((int)m_FillDev);

	}

	public virtual void Deserialize(GenericReader reader)
	{
		int version = reader.ReadInt();

		MaxTechDev = reader.ReadInt();
		m_Stage = (Galaxy.Civilization)reader.ReadInt();
		m_Race = reader.ReadRace();
		m_Home = reader.ReadStarSystem();
		m_Append = reader.ReadBool();
		m_KingName = reader.ReadString();

		m_ID = reader.ReadInt();

		int count = reader.ReadInt();
		m_Planets = new List<SolarPlanet>(count);

		if (count > 0)
			for (int i = 0; i < count; i++)
			{
				SolarPlanet solar = new SolarPlanet(reader.ReadStarSystem());
				solar.pDistance = reader.ReadInt();
				solar.pTurn = reader.ReadInt();

				solar.plPlanets = new List<Planet>();
				int cnt = reader.ReadInt();
				if (cnt > 0)
					for (int j = 0; j < cnt; j++)
					{
						solar.plPlanets.Add(reader.ReadPlanet());
						solar.plPlanets.Sort(new StarSystem.OrbitSorter());
					}

				m_Planets.Add(solar);
				m_Planets.Sort(new HomeSorter());

			}

		count = reader.ReadInt();
		m_Tech = new List<KingdomTech>();

		if (count > 0)
			for (int i = 0; i < count; i++)
			{
				KingdomTech ktech = new KingdomTech((Technology.TechCategory)reader.ReadInt());

				int cnt = reader.ReadInt();
				ktech.plTech = new List<TechByCost>();
				if (cnt > 0)
					for (int j = 0; j < cnt; j++)
					{
						TechByCost tbc = new TechByCost(reader.ReadDouble(), reader.ReadTechnology(), (Technology.TechStatus)reader.ReadInt(), reader.ReadString());
						tbc.pSciPower = reader.ReadDouble();

						ktech.plTech.Add(tbc);
						ktech.plTech.Sort(new CostSorter());
					}

				m_Tech.Add(ktech);
				m_Tech.Sort(new CategorySorter());
			}

		count = reader.ReadInt();
		m_Fleets = new List<Fleet>();

		if (count > 0)
			for (int i = 0; i < count; i++)
			{
				m_Fleets.Add(reader.ReadFleet());
			}

		m_Dispatchables = new Hashtable();
		count = reader.ReadInt();

		if (count > 0)
		{
			Type[] alltypes = Core.GAssembly.GetTypes();

			for (int i = 0; i < count; i++)
			{
				string fullname = reader.ReadString();
				int val = reader.ReadInt();
				Type ty = null;

				for (int j = 0; j < alltypes.Length; j++)
					if (alltypes[j].FullName == fullname)
					{
						ty = alltypes[j];
						break;
					}

				if (ty != null)
					m_Dispatchables.Add(ty, val);

			}
		}

		count = reader.ReadInt();
		m_Dev = new List<TechByCost>();

		if (count > 0)
			for (int i = 0; i < count; i++)
			{
				TechByCost tbc = new TechByCost(reader.ReadDouble(), reader.ReadTechnology(), (Technology.TechStatus)reader.ReadInt(), reader.ReadString());
				tbc.pSciPower = reader.ReadDouble();

				m_Dev.Add(tbc);
			}

		count = reader.ReadInt();
		m_Owned = new List<TechByCost>();

		if (count > 0)
			for (int i = 0; i < count; i++)
			{
				TechByCost tbc = new TechByCost(reader.ReadDouble(), reader.ReadTechnology(), (Technology.TechStatus)reader.ReadInt(), reader.ReadString());
				tbc.pSciPower = reader.ReadDouble();

				m_Owned.Add(tbc);
			}

		count = reader.ReadInt();
		m_Unlocked = new List<Technology>();

		if (count > 0)
			for (int i = 0; i < count; i++)
				m_Unlocked.Add(reader.ReadTechnology());

		m_SciPower = reader.ReadDouble();
		m_Reach = reader.ReadDouble();
		m_Drive = reader.ReadDouble();
		m_DriveBonus = reader.ReadDouble();
		m_Gold = reader.ReadDouble();
		m_Command = reader.ReadDouble();

		m_FillDev = reader.ReadInt();

		Debug.Log(m_Race.RaceType.ToString() + " m_FillDev: " + m_FillDev.ToString());
	}

}